package math.vectorSpace.complexNumbers;

import math.vectorSpace.VectorSpace;
import math.mathExtended.MathExtended;
import myAnnotations.Constant;

/**
 *
 * Fully Tested: No
 * Fully Documented: No
 * Created: 7-May-2012
 * Last Updated: 7-May-2012
 *
 * @author Shimu Wu
 */
public class ComplexNumber implements VectorSpace<ComplexNumber> {

    private final double r, imag;

    private final static double EPSILEON = 1E-4;

    /**
     * Initializes the complex number: 0 + 0i
     */
    public ComplexNumber() {
        this(0, 0);
    }

    /**
     * Initializes the complex number: r + 0i
     * 
     * @param r the r part of the ComplexNumber
     */
    public ComplexNumber(double r) {
        this(r, 0);
    }

    /**
     * Initializes a ComplexNumber to have the same r
     * and imag components as the given c.
     * 
     * @param c 
     */
    public ComplexNumber(ComplexNumber c) {
        this(c.r, c.imag);
    }

    /**
     * Initializes the complex number: r + (imag)i
     * 
     * @param r the r component of the complex number
     * @param imag the imag component of the complex number
     */
    public ComplexNumber(double r, double imag) {
        this.r = r;
        this.imag = imag;
    }

    /**
     * Returns the sum of this ComplexNumber with the given ComplexNumber
     * 
     * @param other
     * @return 
     */
    @Constant
    public ComplexNumber add(ComplexNumber other) {
        // Add the real components, add the imag components
        return new ComplexNumber(this.r + other.r,
                this.imag + other.imag);
    }

    /**
     * Returns the difference between this ComplexNumber and the given 
     * ComplexNumber
     * 
     * @param other
     * @return 
     */
    @Constant
    public ComplexNumber subtract(ComplexNumber other) {
        // Subtract the real components, substract the imag components
        return new ComplexNumber(this.r - other.r,
                this.imag - other.imag);
    }

    /**
     * Returns the product of two ComplexNumbers.
     * 
     * @param other
     * @return 
     */
    @Constant
    public ComplexNumber multiply(ComplexNumber other) {
        /*     
         * z1 * z2 = (a + bi) * (c + di)
         *         = ac + adi + bci + bdi^2
         *         = (ac - bd) + (ad + bc)
         */
        return new ComplexNumber(
                this.r * other.r - this.imag * other.imag,
                this.r * other.imag + this.imag * other.r);
    }

    /**
     * Returns this ComplexNumber squared.
     * 
     * @return 
     */
    @Constant
    public ComplexNumber square() {
        return this.multiply(this);
    }

    /**
     * Returns the conjugate of this ComplexNumber. 
     * 
     * @return 
     */
    @Constant
    public ComplexNumber conjugate() {
        // The conjugate of z = a + bi is the complex number
        // z' such that (z)(z') = a^2 + (b^2)i 
        // since (a-bi)*(a+bi) = a^2 + (b^2)i, z' = a - bi
        return new ComplexNumber(this.r, -this.imag);
    }

    /**
     * Returns the result of dividing two ComplexNumbers.
     * 
     * @param other
     * @return 
     */
    @Constant
    public ComplexNumber divide(ComplexNumber other) {
        /*
         * z1   a + bi   (ac + bd)   (bc - ad)   
         * -- = ------ = --------- + --------- 
         * z2   c + di  (c^2 + d^2) (c^2 + d^2)     
         */
        double temp = MathExtended.sumOfSquares(other.r, other.imag);

        return new ComplexNumber(
                (this.r * other.r + this.imag * other.imag) / temp,
                (this.imag * other.r - this.r * other.imag) / temp);
    }

    /**
     * Returns the magnitude of this ComplexNumber.
     * 
     * @return 
     */
    @Constant
    public double magnitude() {
        return Math.sqrt(MathExtended.sumOfSquares(this.r, this.imag));
    }

    /**
     * Returns the additive inverse of this ComplexNumber.
     * 
     * @return 
     */
    @Constant
    public ComplexNumber invert() {
        return new ComplexNumber(-r, -imag);
    }

    /**
     * Returns this ComplexNumber scaled to the given c
     * 
     * @param c
     * @return 
     */
    public ComplexNumber scale(double c) {
        return new ComplexNumber(r * c, imag * c);
    }

    /**
     * Returns true if this ComplexNumber is equal to the given ComplexNumber
     * (i.e. their real and imaginary components are equal).
     * 
     * @param v2
     * @return 
     */
    public boolean isEqual(ComplexNumber v2) {
        return MathExtended.equal(r, v2.r, EPSILEON)
                && MathExtended.equal(imag, v2.imag, EPSILEON);
    }
}
